using System.Reflection;
using System.Collections;
using System.Text;
using System;

using GD = Godot.GD;
using GodotFile = Godot.FileAccess;
using ModeFlags = Godot.FileAccess.ModeFlags;
using System.Threading.Tasks;

namespace Box.Base {
    public class GDIOStorageFile : IStorageFile,IDisposable,IAsyncDisposable {  
        protected GodotFile file;

        public bool Open(string path,StorageFileMode mode) {
            //bool ret = true;
            if(mode == StorageFileMode.Write) {
                file = GodotFile.Open(path,ModeFlags.WriteRead);
            }
            if(mode == StorageFileMode.Read) {
                file = GodotFile.Open(path,ModeFlags.Read);
            }

            return GodotFile.GetOpenError() == Godot.Error.Ok;
        }

        public void Close() {
            if(file == null) return;
            file.Flush();
            file = null;
        }
        public void Flush() {
            if(file == null) return;
            file.Flush();
        }

        public StorageItemType GetValueType(object value) {
            if(value is int)
            {
                return StorageItemType.Int;
            }
            if((value is double) || (value is float))
            {
                return StorageItemType.Float;
            }
            if(value is bool) {
                return StorageItemType.Boolean;
            }
            if(value is string)
            {
                return StorageItemType.String;
            }
            if(value is Array) {
                return StorageItemType.Array;
            }
            if(value is ArrayList)
            {
                return StorageItemType.AnyArray;
            }
            if(value is Hashtable)
            {
                return StorageItemType.Hashtable;
            }
            if(value is IStorage) {
                return StorageItemType.Object;
            }
            return StorageItemType.Undefined;
        }


        public void WriteItem(int value) {
            file.Store8((byte)StorageItemType.Int);
            file.Store32((uint)value);
        }

        public void WriteItem(double value) {
            file.Store8((byte)StorageItemType.Float);
            file.StoreDouble(value);
        }

        public void WriteItem(string value) {
            byte[] str = Encoding.UTF8.GetBytes(value);
            file.Store8((byte)StorageItemType.String);
            file.Store32((uint)str.Length);
            file.StoreBuffer(str);
        }

        public void WriteItem(object value) {
            if(value is int)
            {
                WriteItem((int)value);
            }
            else if(value is double || value is float)
            {
                WriteItem((double)value);
            }
            else if(value is bool) {
                WriteItem((bool)value);
            }
            else if(value is string)
            {
                WriteItem((string)value);
            }
            else if(value is Array) 
            {
                WriteItem((Array)value);
            }
            else if(value is ArrayList)
            {
                WriteItem((ArrayList)value);
            }
            else if(value is Hashtable)
            {
                WriteItem((Hashtable)value);
            }
            else if(value is IStorage) {
                WriteItem((IStorage)value);
            }
            else {
                throw new StorageFileWriteError($"无法写入\"{value.GetType().FullName}\"类型的值");
            }
        }

        public void WriteItem(ArrayList values) {
            file.Store8((byte)StorageItemType.AnyArray);
            //数组长度
            file.Store32((uint)values.Count);
            for(int i = 0;i < values.Count;i++)
            {
                WriteItem(values[i]);
            }
        }

        public void WriteItem(Hashtable values) {
            file.Store8((byte)StorageItemType.Hashtable);
            file.Store32((uint)values.Count);
            foreach(string key in values.Keys) 
            {
                WriteItem(key);
                WriteItem(values[key]);
            }
        }

        protected void TryReadType(StorageItemType type) {
            StorageItemType read_type = (StorageItemType)file.Get8();
            if(read_type != type) {
                throw new StorageFileReadError($"读取类型错误请检查文件格式及其内容是否正确 需要{type}，但读取的是{read_type}");
            }
        }

        public int ReadInt() {
            return (int)file.Get32();
        }

        public int TryReadIntItem() {
            TryReadType(StorageItemType.Int);
            return  ReadInt();
        }

        public double ReadFloat() {
            return file.GetDouble();
        }

        public double TryReadFloatItem() {
            TryReadType(StorageItemType.Float);
            return ReadFloat();
        }


        public string ReadString() {
            long len = (int)file.Get32();
            byte[] str = file.GetBuffer(len);
            return Encoding.UTF8.GetString(str);
        }

        public string TryReadStringItem() {
            TryReadType(StorageItemType.String);
            return ReadString();
        }

        public object ReadNext() {
            StorageItemType read_type = (StorageItemType)file.Get8();
            return ReadNext(read_type);
        }

        public object ReadNext(StorageItemType type) {
            switch(type) {
                case StorageItemType.Int : return (int)ReadInt();
                case StorageItemType.Float : return (double)ReadFloat();
                case StorageItemType.String : return (string)ReadString();
                case StorageItemType.Boolean : return (bool)ReadBoolean();
                case StorageItemType.Array : return (Array)ReadArray();
                case StorageItemType.AnyArray : return (ArrayList)ReadAnyArray();
                case StorageItemType.Hashtable : return (Hashtable)ReadHashtable();
                case StorageItemType.Object : return (IStorage)ReadStorageObject();
                default : {
                    throw new StorageFileReadError($"读取错误 -> 读取到的类型为{type}");
                }
            }
        }

        public Hashtable ReadHashtable() {
            Hashtable hashtable = new Hashtable();
            int len = (int)file.Get32();
            for(int i = 0;i<len;i++) {
                string key = TryReadStringItem();
                object value = ReadNext();

                hashtable[key] = value;
            }
            return hashtable;
        }

        public Hashtable TryReadHashtableItem() {
            TryReadType(StorageItemType.Hashtable);
            return ReadHashtable();
        }

        public ArrayList ReadAnyArray() {
            ArrayList array = new ArrayList();
            int len = (int)file.Get32();
            for(int i = 0;i<len;i++) {
                object value = ReadNext();
                array.Add(value);
            }
            return array;
        }

        public ArrayList TryReadAnyArrayItem() {
            TryReadType(StorageItemType.AnyArray);
            return ReadAnyArray();
        }

        public void Write(int value) {
            file.Store32((uint)value);
        }
        public void Write(double value) {
            file.StoreDouble(value);
        }

        public void Write(string value) {
            byte[] str = Encoding.UTF8.GetBytes(value);
            file.Store32((uint)str.Length);
            file.StoreBuffer(str);
        }

        public void Write(object value) {
            if(value is int)
            {
                Write((int)value);
            }
            else if(value is double || value is float)
            {
                Write((double)value);
            }
            else if(value is string)
            {
                Write((string)value);
            }
            else if(value is Array) {
                Write((Array)value);
            }
            else if(value is ArrayList)
            {
                Write((ArrayList)value);
            }
            else if(value is Hashtable)
            {
                Write((Hashtable)value);
            }
            else {
                throw new StorageFileWriteError($"无法写入\"{value.GetType().Name}\"类型的值");
            }
        }

        public void Write(ArrayList values) {
            //数组长度
            file.Store32((uint)values.Count);
            for(int i = 0;i < values.Count;i++)
            {
                WriteItem(values[i]);
            }
        }

        public void Write(Hashtable values) {
            file.Store32((uint)values.Count);
            foreach(string key in values.Keys) 
            {
                WriteItem(key);
                WriteItem(values[key]);
            }
        }

        public void Write(IStorage value)
        {
            Type t = value.GetType();
            value.onStorageWriteReady();
            Write(t.FullName);
            FieldInfo[] fields = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            foreach(FieldInfo field in fields) {
                StorageAttribute storage_attr = field.GetCustomAttribute<StorageAttribute>();
                if(storage_attr != null) {
                    object v = field.GetValue(value);
                    if(StorageFileHelper.IsStorageType(v)) {
                        WriteItem(v);
                    } else {
                        throw new StorageFileWriteError($"无法写入{field.FieldType.FullName}类型的数据");
                    }
                }
            }
            value.onStorageWriteComplete();
        }

        public IStorage ReadStorageObject()
        {
            string class_name = ReadString();
            Assembly assembly = Assembly.GetExecutingAssembly();
            object obj = assembly.CreateInstance(class_name);
            if(obj == null) {
                throw new StorageFileReadError($"找不到名为{class_name}的对象类");
            }
            
            IStorage storage = obj as IStorage;
            storage.onStorageReadReady();
            Type t = storage.GetType();
            FieldInfo[] fields = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            foreach(FieldInfo field in fields) {
                StorageAttribute storage_attr = field.GetCustomAttribute<StorageAttribute>();
                if(storage_attr != null) {
                    object r = ReadNext();
                    field.SetValue(storage,r);
                }
            }
            storage.onStorageReadComplete();

            return storage;
        }

        public void WriteItem(IStorage value)
        {
            file.Store8((byte)StorageItemType.Object);
            Write(value);
        }

        public IStorage TryReadStorageItem()
        {
            TryReadType(StorageItemType.Object);
            return ReadStorageObject();
        }

        public void Write(bool value)
        {
            Write(value ? 1 : 0);
        }

        public bool ReadBoolean()
        {
            return ReadInt() == 1;
        }

        public void WriteItem(bool value)
        {
            file.Store8((byte)StorageItemType.Boolean);
            Write(value);
        }

        public bool TryReadBooleanItem()
        {
            TryReadType(StorageItemType.Boolean);
            return ReadBoolean();
        }

        public void Write(Array values)
        {
            int len = values.Length;
            file.Store32((uint)len);
            if(len == 0) return;
            StorageItemType type = GetValueType(values.GetValue(0));
            if(type == StorageItemType.Undefined) {
                throw new StorageFileWriteError($"无法写入\"{values.GetValue(0).GetType().Name}\"类型的值");
            }
            file.Store8((byte)type);
            foreach(object v in values) {
                Write(v);
            }
        }

        public Array ReadArray()
        {
            int len = (int)file.Get32();
            StorageItemType type = (StorageItemType)file.Get8();
            object[] array = new object[len];
            for(int i = 0;i<len;i++) {
                array[i] = ReadNext(type);
            }
            return array;
        }

        public void WriteItem(Array values)
        {
            file.Store8((byte)StorageItemType.Array);
            Write(values);
        }

        public Array TryReadArrayItem()
        {
            TryReadType(StorageItemType.Array);
            return ReadArray();
        }

        public void Dispose()
        {
            if(file != null) {
                file.Flush();
                file.Dispose();
            }
        }

        public ValueTask DisposeAsync()
        {
            Dispose();
            return new ValueTask();
        }
    }
}